package com.chenjt.jvm.first.nine_String;

/**
 * 注释：
 * 字符串拼接操作
 *
 * @author chenjt
 * @date 2025/6/13 9:37
 */
public class StringTest5 {

    public static void main(String[] args) {
//        test1();
//        test2();
//        test3();
//        test4();

        /**
         * 体会执行效率:
         * 通过StringBuilder的append()的方式添加字符串的效率
         * 远高于使用String字符串拼接方式
         *
         * 详情：
         * 1.StringBuilder的append()方式：自始至终只创建过一个StringBuilder对象
         *   使用String的字符串拼接方式：创建多个StringBuilder和String的对象
         * 2.使用String的字符串拼接方式：内存中由于创建了较多的StringBuilder和String对象，
         *    内存占用更大，如果进行GC，需要花费额外的时间
         *
         * 改进的空间
         * 在实际开发中，如果基本确定要前前后后添加的字符串长度不高于某个限定值highLevel
         * 的情况下，建议使用构造器实例化：
         * StringBuilder s = new StringBuilder(highLevel)// new char[highLevel]
         *
         * 因为StringBuilder内部是创建一个char[]，初始长度为16，如果后续大于16，
         * 会创建一个新的长度更长的数组，将旧数组内容复制过来，当扩容次数多了，之前的
         * 旧数组就需要进行垃圾回收
         *
         *
         */
//        method1(100000);// 8603ms
        method2(100000);// 4ms

    }

    /**
     * 字符串拼接
     * @param highLevel
     */
    private static void method1(int highLevel) {
        long start = System.currentTimeMillis();
        String src = "";
        for (int i = 0; i < highLevel; i++) {
            src = src + "a";// 每次循环都会创建一个StringBuilder、String
        }
        long end = System.currentTimeMillis();
        System.out.println("耗时：" + (end - start) + "ms");
    }


    /**
     * StringBuilder的append()方式
     * @param highLevel
     */
    private static void method2(int highLevel) {
        long start = System.currentTimeMillis();

        StringBuilder src = new StringBuilder();
        for (int i = 0; i < highLevel; i++) {
            src.append("a");
        }

        long end = System.currentTimeMillis();
        System.out.println("耗时：" + (end - start) + "ms");
    }





    /**
     * 常量与常量的拼接结果是在常量池，原理是编译器优化
     */
    private static void test1(){
        String s1 = "a" + "b" + "c";// 等同于"abc"
        String s2 = "abc";// "abc"一定是放在字符串常量池中，将此地址赋给s2

        /**
         * 最终.java编译成.class，再执行.class
         */
        System.out.println(s1 == s2);// true
        System.out.println(s1.equals(s2));// true
    }

    /**
     * 只要其中有一个是变量，结果就在堆中。
     * 变量拼接的原理是StringBuilder底层原理，将变量拼接成新的字符串，拼接完以后，将结果放在堆中。
     */
    private static void test2(){
        String s1 = "javaEE";
        String s2 = "hadoop";


        String s3 = "javaEEhadoop";
        String s4 = "javaEE" + "hadoop";// 编译期优化
        // 如果拼接符号的前后出现了变量，则相当于在堆空间中new String()，具体的内容为拼接的结果：javaEEhadoop
        String s5 = s1 + "hadoop";
        String s6 = "javaEE" + s2;
        String s7 = s1 + s2;

        System.out.println(s3 == s4);// true
        System.out.println(s3 == s5);// false
        System.out.println(s3 == s6);// false
        System.out.println(s3 == s7);// false
        System.out.println(s5 == s6);// false
        System.out.println(s5 == s7);// false
        System.out.println(s6 == s7);// false
        /**
         * intern()：判断字符串常量池中是否存在javaEEhadoop
         * 是：返回字符串常量池中javaEEhadoop的地址
         * 否：在字符串常量池中创建一个javaEEhadoop，并返回此地址
         */
        String s8 = s6.intern();
        System.out.println(s3 == s8);// true

    }


    private static void test3(){
        String s1 = "a";
        String s2 = "b";
        String s3 = "ab";
        /**
         * 如下的s1 + s2的执行细节（这里的变量是随便写的，jdk底层不一定叫s）
         * 1.StringBuilder s = new StringBuilder();
         * 2.s.append("a");
         * 3.s.append("b");
         * 4.s.toString(); --> 约等于 new String("ab")
         *
         * 补充：在jdk5.0之后使用的是StringBuilder
         * 在jdk5.0之前使用的是StringBuffer
         *
         */
        String s4 = s1 + s2;
        System.out.println(s3 == s4);// false
    }

    /**
     * 1.字符串拼接操作不一定使用的是StringBuilder
     *    如果拼接符号左右两边都是字符串常量或常量引用，则仍然使用编译期优化，
     *    即非StringBuilder的方式
     * 2.针对于final修饰类、方法、基本数据类型、引用数据类型的量的结构时，
     *  能使用上final时建议使用，因为final在编译时就已经把内容确定下来了
     */
    private static void test4(){
        final String s1 = "a";
        final String s2 = "b";
        String s3 = "ab";
        String s4 = s1 + s2;

        System.out.println(s3 == s4);// true

    }

    // 练习
    private static void test5(){
        String s1 = "javaEEhadoop";
        String s2 = "javaEE";
        String s3 = s2 + "hadoop";
        System.out.println(s1 == s3);// false

        final String s4 = "javaEE";// s4：常量
        String s5 = s4 + "hadoop";
        System.out.println(s1 == s5);// true
    }


}

